home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / src / uucp / rmail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-28  |  31.9 KB  |  1,430 lines

  1. /*
  2.  *                              R M A I L . C
  3.  *
  4.  *      Developed from the Berkeley mail program of the same name
  5.  *      by Mike Obrien at RAND to run with the MMDF mail system.
  6.  *      Rewritten by Doug Kingston, US Army Ballistics Research Laboratory
  7.  *      Hacked a lot by Steve Bellovin (smb@unc)
  8.  *
  9.  *      This program runs SETUID to Opr so that it can set effective and
  10.  *      real [ug]ids to mmdflogin.
  11.  *
  12.  *      Steve Kille -  Aug 84
  13.  *              Take machete to it.  Use protocol mode,
  14.  *              and always go thru submit - phew
  15.  *      Lee McLoughlin Oct 84.
  16.  *              Address munges some header lines into 822 format.
  17.  *    Peter Collinson Dec 85.
  18.  *        Lee's version was dependent on 
  19.  *        (a) the uucp channel
  20.  *        (b) the domain tables associated with the uucp channel
  21.  *        This version costs more cycles (but there is generally
  22.  *        only one of these running on a machine at a time) and
  23.  *        uses mmdf's tables to dis-assemble bang routes converting
  24.  *        them into a domain address.
  25.  *    Peter Collinson March 87.
  26.  *        Add the CITATION ifdef to allow shortened messages to be
  27.  *        returned on failure. The value of CITATION is the number of
  28.  *        message lines returned. Change the format of bounced messages
  29.  *        to be more like the rest of MMDF. Add a line formatter so that
  30.  *        error messages are folded over several lines.
  31.  *        Add sundry (void)'s to stop the C compiler on the HLH Orion
  32.  *        complaining.
  33.  *    Peter Collinson June 87
  34.  *        The existing version send bang addresses into submit
  35.  *        from the rmail command line. This breaks other channels
  36.  *        which do not know about !'s. So, alter any PURE bang
  37.  *        address into a 733 % % % @ sequence allowing the uucp
  38.  *        channel to change these back to ! ! ! and other channels
  39.  *        to deal with them appropriately.
  40.  *    
  41.  *    Jacob Gore May 89
  42.  *        Instead of converting "a!b!c!user" to "user%c%b@a", which
  43.  *        does NOT get converted back by the uucp channel, just convert
  44.  *        it to pseudo-RFC-976 form, "b!c!user@a" ("pseudo-" because
  45.  *        "a" by itself may not be a valid internet hostname).
  46.  */
  47. #undef  RUNALON
  48.  
  49. #include "util.h"
  50. #include "mmdf.h"
  51. #include "ap.h"
  52. #include "ch.h"
  53. #include "dm.h"            /* needed for host_equal */
  54. #include <pwd.h>
  55. #include <signal.h>
  56. #include <sys/stat.h>
  57.  
  58. #define    VERSION        "4.0"
  59.  
  60. #define    CITATION    6    /* Added Peter C March 1987 */
  61.                 /* when bouncing only bounce part of */
  62.                 /* a message */
  63.  
  64. #define NAMESZ        256    /* Limit on component name size. LMCL was 64 */
  65. #define    MAXADRS     6    /* Max no. of adrs to output on a single line */
  66. #define    MAXERRSTASH    5
  67. #define    ungetline(s)    ((void) strcpy(tmpline,s),usetmp=1)
  68.  
  69. typedef    struct {
  70.     char    *hname;
  71.     int    hfound;
  72. } Hdrselect;
  73.  
  74. typedef    struct    {        /* Where to save returnerror messages */
  75.     char    e_s_dest[LINESIZE];
  76.     struct    rp_bufstruct e_s_reason;
  77. } Err_stash;
  78.  
  79. typedef    struct {
  80.     char    *o_host;        /* Host name */
  81.     char    *o_official;        /* Official name */
  82. } Official;
  83.  
  84. Hdrselect hdrselect[] = {
  85. #define HDATE    0
  86.     "date",        0,    /* this is a little naughty - we need */
  87.                 /* to register that we have had this */
  88.                 /* but not address munge it */
  89.                 /* so it has token 0 */
  90. #define HFROM    1
  91.     "from",        0,
  92. #define HTO    2
  93.     "to",        0,
  94. #define HCC    3
  95.     "cc",        0,
  96. #define HBCC    4
  97.     "bcc",        0,
  98. #define HSENDER    5            
  99.     "sender",    0,
  100. #define HREPLY    6
  101.     "reply-to",    0,
  102. #define HRFROM    7
  103.     "resent-from",    0,
  104. #define HRSENDER    8            
  105.     "resent-sender",0,
  106. #define HRTO    9
  107.     "resent-to",    0,
  108.     0,    0
  109. };
  110.  
  111. Err_stash    e_stash[MAXERRSTASH];
  112.  
  113. extern    LLog    *logptr;
  114. extern    char    *locfullname;
  115. extern    char    *Uchan;
  116. extern    char    *mmdflogin;
  117. extern    char    *sitesignature;
  118. extern    char    *supportaddr;
  119. extern    char    *optarg;
  120.  
  121. extern char *index();
  122. extern char *rindex();
  123. extern char *getenv();    /* get the Accounting system from the environment */
  124. extern char *strdup();
  125. extern char *malloc();
  126. extern char *compress();
  127.  
  128. extern    int    optind;
  129.  
  130. FILE    *rm_msgf;        /* temporary out for message text */
  131. Chan    *chanptr;
  132. char    Msgtmp[] = "/tmp/rmail.XXXXXX";
  133. char    *next = NULL;        /* Where nextchar() gets the next char from */
  134. char    rm_date[LINESIZE];    /* date of origination from uucp header */
  135. char    rm_from[LINESIZE];    /* accumulated path of sender */
  136. char    origsys[NAMESZ];    /* originating system */
  137. char    origpath[LINESIZE];    /* path from us to originating system */
  138. char    Mailsys[LINESIZE];    /* Mail system signature */
  139. char    tmpline[LINESIZE];    /* Temp buffer for continuations and such */
  140. char    adrs[LINESIZE];        /* Partly munged addresses go here */
  141. char    linebuf[LINESIZE];    /* scratchpad */
  142. char    nextdoor[LINESIZE];    /* The site nextdoor who sent the mail */
  143. char    uchan[LINESIZE];    /* Channel to use for submission */
  144.  
  145. int    Tmpmode = 0600;
  146. int    usetmp = 0;        /* whether tmpline should be used */
  147. int    e_s_count;
  148. int    e_s_other;
  149. int    debug;
  150.  
  151.  
  152. main(argc, argv)
  153. char **argv;
  154. {
  155.     struct    passwd    *pw, *getpwnam(), *getpwuid();
  156.     char        *fromptr, *cp, *d;
  157.     char        fromwhom[NAMESZ];    /* user on remote system */
  158.     char        sys[NAMESZ];    /* an element in the uucp path */
  159.     int        fd;
  160.  
  161.     mmdf_init(argv[0]);
  162.  
  163.     while ((fd = getopt(argc, argv, "df:")) != EOF) {
  164.         switch (fd) {
  165.         case 'd':
  166.             debug = 1;
  167.             break;
  168.  
  169.         case 'f':
  170.             close(0);
  171.             open(optarg, 0);
  172.             break;
  173.         }
  174.     }
  175.  
  176.     if (optind == argc) {
  177.         fprintf(stderr, "Usage: rmail user [user ...]\n");
  178.         exit(1);
  179.     }
  180.  
  181. #if 0
  182.     /*
  183.      * point standard error and standard out at /dev/null
  184.      */
  185.     if (debug == 0) {
  186.         close(1);
  187.         open("/dev/null", 1);
  188.         close(2);
  189.         dup(1);
  190.     }
  191. #endif
  192.     
  193.     umask(0);
  194.  
  195.     /*
  196.      * First, change effective and real UID/GID into Mailsys owner
  197.      */
  198.     if ((pw = getpwnam(mmdflogin)) == NULL) {
  199.         fprintf(stderr, "Cannot find mmdflogin\n");
  200.         exit(99);
  201.     }
  202.     setgid(pw->pw_gid);
  203.     setuid(pw->pw_uid);
  204.  
  205.     (void) sprintf(Mailsys, "%s <%s@%s>",
  206.         sitesignature, mmdflogin, locfullname);
  207.  
  208.     /*
  209.      * Create temp file for body of message
  210.      */
  211.     mktemp(Msgtmp);
  212.     if ((fd = creat(Msgtmp, Tmpmode)) < 0)
  213.         bomb("Can't create %s\n", Msgtmp);
  214.     close(fd);
  215.  
  216.     if ((rm_msgf = fopen(Msgtmp, "r+")) == NULL)
  217.         bomb("Can't reopen %s\n", Msgtmp);
  218.  
  219.     unlink(Msgtmp);
  220.  
  221.     /*
  222.      * Unravel the From and >From lines at the head of the message
  223.      * to work who sent it, the path it took to get here and when
  224.      * it was sent.
  225.      *
  226.      * Some or all of this info may be ignore depending on the 822 header
  227.      * info given.
  228.      */
  229.     for (;;) {
  230.         if (gets(linebuf) == NULL)
  231.             break;
  232.         if (strncmp(linebuf, "From ", 5) &&
  233.             strncmp(linebuf, ">From ", 6))
  234.             break;
  235.  
  236.         cp = index(linebuf, ' ');    /* start of name */
  237.         fromptr = ++cp;
  238.         cp = index(cp, ' ');        /* cp at end of name */
  239.         *cp++ = '\0';            /* term. name, cp at date */
  240.         (void) strcpy(fromwhom, fromptr);
  241.         while (isspace(*cp)) cp++;    /* Skip any ws */
  242.         /*
  243.          * The date is the rest of the line ending at \0 or remote
  244.          */
  245.         d = rm_date;
  246.         while (*cp && strncmp(cp, " remote from ", 13))
  247.             *d++ = *cp++;
  248.         *d = '\0';
  249.  
  250.         for (;;) {
  251.             cp = index(cp+1, 'r');
  252.             if (cp == NULL) {
  253.                 cp = rindex(fromwhom, '!');
  254.                 if (cp != NULL) {
  255.                     char *p;
  256.                     *cp = '\0';
  257.                     p = rindex(fromwhom, '!');
  258.                     if (p != NULL)
  259.                         (void) strcpy(origsys, p+1);
  260.                     else
  261.                         (void) strcpy(origsys, fromwhom);
  262.                     (void) strcat(rm_from, fromwhom);
  263.                     (void) strcat(rm_from, "!");
  264.                     (void) strcpy(fromwhom, cp+1);
  265.                     goto out;
  266.                 }
  267.                 /*
  268.                  * Nothing coherent found - so look in
  269.                  * environment for ACCTSYS
  270.                  */
  271.                 if ((cp = getenv("ACCTSYS")) && *cp) {
  272.                     (void) strcpy(origsys, cp);
  273.                     (void) strcat(rm_from, cp);
  274.                     (void) strcat(rm_from, "!");
  275.                     goto out;
  276.                 }
  277.                 cp = "remote from somewhere";
  278.             }
  279.             if (strncmp(cp, "remote from ", 12) == 0)
  280.                 break;
  281.         }
  282.  
  283.         sscanf(cp, "remote from %s", sys);
  284.         (void) strcat(rm_from, sys);
  285.         (void) strcpy(origsys, sys);    /* Save for quick ref. */
  286.         (void) strcat(rm_from, "!");
  287.     }
  288. out:
  289.     if (fromwhom[0] == '\0')        /* No from line, illegal */
  290.         bomb("No from lines in message\n");
  291.  
  292.     ungetline(linebuf);
  293.  
  294.     /*
  295.      * Complete the from field
  296.      */
  297.     (void) strcat(rm_from, fromwhom);
  298.     
  299.     /*
  300.      * A uk special - see if the first two components of the constructed
  301.      * bang address are in fact the same site. If so replace by their
  302.      * official name
  303.      */
  304.     domain_cross(rm_from);
  305.  
  306.     /*
  307.      * Save a copy of the path to the original site.
  308.      * This is all the the path we were given - the user name after
  309.      * the last !. NOTE: We keep the trailing !, hence the +1.
  310.      */
  311.     (void) strcpy(origpath, rm_from);
  312.     *(rindex(origpath, '!') + 1) = '\0';
  313.  
  314.     /*
  315.      * Savepath is given a copy of the immediate neighbour
  316.      */
  317.     if ((d = index(rm_from, '!')) != NULL) {
  318.         *d = '\0';
  319.         (void) strcpy(nextdoor, rm_from);
  320.         *d = '!';
  321.     }
  322.     else
  323.         (void) strcpy(nextdoor, rm_from);
  324.     
  325.     /*
  326.      * Find the channel depending in the nextdoor site
  327.      */
  328.     set_channel(nextdoor);
  329.     ch_llinit(chanptr);
  330.  
  331.     /*
  332.      * Convert the from to Arpa format and leave it in from
  333.      */
  334.     fromcvt(rm_from, fromwhom);
  335.     (void) strcpy(rm_from, fromwhom);
  336.  
  337.     if (debug)
  338.         printf("from=%s, origpath=%s, date=%s\n",
  339.             rm_from, origpath, rm_date);
  340.  
  341.     msgfix(rm_from, rm_date);
  342.  
  343.     xsubmit(rm_from, argv, argc);
  344.  
  345.     exit(0);
  346. }
  347.  
  348. /*
  349.  * Munge the message header.
  350.  *
  351.  * All header lines with addresses in are munged into 822 format.
  352.  * If no "From:" line is given supply one based on the UUCP Froms, do the
  353.  * same if no "Date:".
  354.  */
  355. /*
  356.  * Is this header in the list of those to have their addresses munged?
  357.  * returnheader value offset value
  358.  * notice that it returns 0 for the date header
  359.  */
  360. shouldmunge(name)
  361. char *name;
  362. {
  363.     register    Hdrselect    *h;
  364.  
  365.     if (debug)
  366.         printf("in shouldmunge with '%s'\n", name);
  367.  
  368.     for (h = hdrselect; h->hname != NULL; h++)
  369.         if (lexequ(h->hname, name)) {
  370.             h->hfound++;
  371.             return(h - hdrselect);
  372.         }
  373.  
  374.     return(0);
  375. }
  376.  
  377. /*
  378.  * If this is a header line then grab the name of the header and stuff it
  379.  * into 'name' then return a pointer to the actually body of the line.
  380.  * Otherwise return NULL.
  381.  *
  382.  * NOTE: A header is a line that begins with a word formed of alphanums and
  383.  * dashes (-) then possibly some whitespace then a colon (:).
  384.  */
  385. char *
  386. grabheader(s, name)
  387. register char *s, *name;
  388. {
  389.     char    tmpbuf[LINESIZE];
  390.  
  391.     /*
  392.      * Copy the name into name
  393.      */
  394.     while (isalpha(*s) || isdigit(*s) || *s == '-')
  395.         *name++ = *s++;
  396.     *name = '\0';
  397.  
  398.     /*
  399.      * Skip any whitespace
  400.      */
  401.     while (isspace(*s))
  402.         s++;
  403.  
  404.     /*
  405.      * This is a header if the next char is colon
  406.      */
  407.     if (*s == ':') {
  408.         s++;
  409.         /* 
  410.          * This is probably illegal but we fail horribly if it
  411.          * happens - so we will guard against it
  412.          */
  413.         if (*s != ' ' && *s != '\t' && *s != '\0') {
  414.             /* we need to add a space */
  415.             (void) strcpy(tmpbuf, s);
  416.             (void) sprintf(s, " %s", tmpbuf);
  417.         }
  418.         return(s);    /* Return a pointer to the rest of the line */
  419.     }
  420.  
  421.     return(NULL);
  422. }
  423.  
  424. msgfix(from, date)
  425. char *from, *date;
  426. {
  427.     register    char    *s;
  428.     register    int    c;
  429.             char    *rest, *lkp;
  430.             char    name[LINESIZE], tmpbuf[LINESIZE];
  431.             char    *grabline();
  432.             int    haveheader = 0;    /* Do I have a header? */
  433.             int    headertoken;
  434.  
  435.     /*
  436.      * Loop through all the headers
  437.      */
  438.     while (1) {
  439.     
  440.         if (debug)
  441.             printf("in msgfix about to grabline\n");
  442.  
  443.         s = grabline();
  444.  
  445.         if (debug)
  446.             printf("got '%s'\n", s);
  447.  
  448.         /*
  449.          * Is this the end of the header?
  450.          */
  451.         if (*s == '\0' || feof(stdin))
  452.             break;
  453.  
  454.  
  455.         /*
  456.          * Is this a continuation line?
  457.          */
  458.         if (haveheader && isspace(*s)) {
  459.             /* Note: Address munged headers handled specially */
  460.             fprintf(rm_msgf, "%s\n", s);
  461.         }
  462.         else {
  463.             /*
  464.              * Grab the header name from the line
  465.              */
  466.             if ((rest = grabheader(s, name)) == NULL)
  467.                 /* Not a header therefore all headers done */
  468.                 break;
  469.  
  470.             haveheader = 1;
  471.  
  472.             /*
  473.              * Should I address munge this?
  474.              */
  475.             if (headertoken = shouldmunge(name)) {
  476.                 char *finalstr;
  477.                 finalstr = "\n";
  478.                 fprintf(rm_msgf, "%s: ", name);
  479.                 /*
  480.                  * deal specially with From lines
  481.                  * the parser loses comments so we retain
  482.                  * them specially here
  483.                  */
  484.                 if (headertoken == HFROM
  485.                  || headertoken == HRFROM) {
  486.                     if (lkp = index(rest, '<')) {
  487.                         *lkp = '\0';
  488.                         if (*rest == ' ')
  489.                             fprintf(rm_msgf, "%s<", rest+1);
  490.                         else
  491.                             fprintf(rm_msgf, "%s<", rest);
  492.                         *lkp = '<';
  493.                         finalstr = ">\n";
  494.                         (void) strcpy(tmpbuf, lkp);
  495.                         /*
  496.                          * notice that we copy from the
  497.                          * '<' to start with a space
  498.                          */
  499.                         tmpbuf[0] = ' ';
  500.                         rest = tmpbuf;
  501.                         lkp = rindex(rest, '>');
  502.                         if (lkp)
  503.                             *lkp = '\0';
  504.                     }
  505.                     if (lkp = index(rest, '(')) {
  506.                         *lkp = '\0';
  507.                         (void) sprintf(tmpbuf, " (%s\n", lkp+1);
  508.                         finalstr = tmpbuf;
  509.                     }
  510.                 }
  511.                 hadr_munge(rest);
  512.                 fputs(finalstr, rm_msgf);
  513.             }
  514.             else
  515.                 fprintf(rm_msgf, "%s\n", s);
  516.         }
  517.     }
  518.  
  519.     /*
  520.      * No From: line was given, create one based on the From's
  521.      */
  522.     if (hdrselect[HFROM].hfound == 0) {
  523.         fprintf(rm_msgf, "From: %s\n", from);
  524.     }
  525.  
  526.     /*
  527.      * No Date: line was given, create one based on the From's
  528.      */
  529.     if (hdrselect[HDATE].hfound == 0) {
  530.         datecvt(date, tmpbuf);        /* Convert from uucp -> Arpa */
  531.         fprintf(rm_msgf, "Date: %s\n", tmpbuf);
  532.     }
  533.  
  534.     /*
  535.      * Copy the rest of the file, if there is any more
  536.      */
  537.     if (!feof(stdin)) {
  538.         /*
  539.          * If the first line of the message isn't blank
  540.          * output the blank separator line.
  541.          */
  542.         if (*s)
  543.             putc('\n', rm_msgf);
  544.         fprintf(rm_msgf, "%s\n", s);
  545.         while ((c = getchar()) != EOF) {
  546.             if (ferror(stdin))
  547.                 fputs("\n  *** Problem during receipt from UUCP ***\n", rm_msgf);
  548.             putc(c, rm_msgf);
  549.         }
  550.     }
  551. }
  552.  
  553. char *
  554. grabline()
  555. {
  556.     if (debug)
  557.         printf("in grabline ");
  558.  
  559.     /*
  560.      * Grab the next line.
  561.      * Remembering this might be the tmpline
  562.      */
  563.     if (usetmp) {
  564.         if (debug)
  565.             printf("using tmpline ");
  566.         (void) strcpy(linebuf, tmpline);
  567.         usetmp = 0;
  568.     }
  569.     else
  570.         gets(linebuf);
  571.  
  572.     /* Anything wrong? */
  573.     if (ferror(stdin))
  574.         fputs("\n  *** Problem during receipt from UUCP ***\n", rm_msgf);
  575.  
  576.     if (debug)
  577.         printf("returning '%s'\n ", linebuf);
  578.  
  579.     return(linebuf);
  580.  
  581. }
  582.  
  583. nextchar()
  584. {
  585.     register    char    *s;    /* scratch pointer. */
  586.  
  587.     if (next != NULL && *next != '\0') {
  588.         if (debug)
  589.             printf("<%c>", *next);
  590.         return(*next++);
  591.     }
  592.  
  593.     /*
  594.      * The last buffer is now empty, fill 'er up
  595.      */
  596.     next = grabline();
  597.  
  598.     if (feof(stdin) || !isspace(*next) || *next == '\0') {
  599.         /* Yipee! We've reached the end of the address list. */
  600.         ungetline(next);
  601.         next = NULL;            /* Force it to read buffer */
  602.         return(-1);
  603.     }
  604.  
  605.     /* Zap excess whitespace */
  606.     (void) compress(next, adrs);
  607.  
  608.     /*
  609.      * This may be a slightly duff list generated by some of the
  610.      * UNIX mailers eg: "x y z" rather than "x,y,z"
  611.      * If it is in this ^^^^^^^ convert to  ^^^^^^^ 
  612.      * NOTE: This won't handle list: "x<x@y> y<y@z>" conversions!
  613.      */
  614.     if (index(adrs, ' ') != NULL &&
  615.         index(adrs, ',') == NULL &&
  616.         index(adrs, '<') == NULL &&
  617.         index(adrs, '(') == NULL &&
  618.         index(adrs, '"') == NULL) {
  619.         for (s = adrs; *s; s++)
  620.             if (*s == ' ')
  621.                 *s = ',';
  622.     }
  623.     next = adrs;
  624.     /* This is a continuation line so return a seperator just in case */
  625.     return(',');
  626. }
  627.  
  628. /*
  629.  * Munge an address list thats part of a header line. Try and get the
  630.  * ap_* (MMDF) routines to do as much as possible for us.
  631.  */
  632. hadr_munge(list)
  633. char *list;
  634. {
  635.     AP_ptr    the_addr;        /* ---> THE ADDRESS <--- */
  636.     int    adrsout = 0;        /* How many address have been output */
  637.  
  638.     ungetline(list);
  639.  
  640.     while (1) {
  641.         AP_ptr ap_pinit();
  642.  
  643.         the_addr = ap_pinit(nextchar);
  644.  
  645.         if (the_addr == (AP_ptr)NOTOK)
  646.             bomb("cannot initialise address parser!");
  647.  
  648.         ap_clear();
  649.  
  650.         switch(ap_1adr()) {
  651.         case NOTOK:
  652.             /* Something went wrong!! */
  653.             (void) ap_sqdelete(the_addr, (AP_ptr)NULL);
  654.             ap_free(the_addr);
  655.             /*
  656.              * Emergency action in case of parser break down:
  657.              * print out the rest of the line (I hope).
  658.              */
  659.             fprintf(rm_msgf, "%s", tmpline);
  660.             continue;
  661.  
  662.         case OK:
  663.             /*
  664.              * I've got an address to output!
  665.              * Output a comma seperator between the addresses
  666.              * and a newline every once in a while to make
  667.              * sure the lines aren't too long
  668.              */
  669.             if (adrsout++)
  670.                 fprintf(rm_msgf, ",%s ", adrsout%MAXADRS?"":"\n");
  671.  
  672.             /* Munge will do all the work to output it */
  673.             munge(the_addr);
  674.  
  675.             /* Reclaim the space */
  676.             (void) ap_sqdelete(the_addr, (AP_ptr)NULL);
  677.             ap_free(the_addr);
  678.  
  679.             if (debug)
  680.                 printf("munged and space freed\n");
  681.             break;
  682.  
  683.         case DONE:
  684.             if (debug)
  685.                 printf("hadr_munge all done\n");
  686.  
  687.             /* Reclaim the space */
  688.             (void) ap_sqdelete(the_addr, (AP_ptr)NULL);
  689.             ap_free(the_addr);
  690.  
  691.             return;
  692.         }
  693.     }
  694. }
  695.  
  696. /*
  697.  * We now have a single address in the_addr to output.
  698.  */
  699. munge(the_addr)
  700. AP_ptr the_addr;
  701. {
  702.     AP_ptr    local, domain, route;
  703.     char    *s, *frees;
  704.     char    adr[LINESIZE];
  705.     char    *ap_p2s();
  706.  
  707.     if (debug)
  708.         printf("in munge with: ");
  709.  
  710.     /*
  711.      * Find where the important bits begin in the tree
  712.      */
  713.     (void) ap_t2parts(the_addr, (AP_ptr *)NULL, (AP_ptr *)NULL,
  714.             &local, &domain, &route);
  715.  
  716.     /*
  717.      * Convert from the tree back into a string
  718.      */
  719.     frees = s = ap_p2s((AP_ptr)NULL, (AP_ptr)NULL, local, domain, route);
  720.  
  721.     if (debug)
  722.         printf("%s\n", s);
  723.  
  724.     /* Is it a uucp style address? */
  725.     if (domain == (AP_ptr) 0 && index (s, '!') != (char *)NULL) {
  726.         char    adr2[LINESIZE];
  727.  
  728.         (void) strcpy(adr, s);
  729.  
  730.         /*
  731.          * Stick the path it took to get here at the start.
  732.          * But try to avoid any duplicates by overlapping
  733.          * the matching parts of the address. Eg:
  734.          *    adr = 'x!y!z'  path = 'a!b!x!y!'
  735.          *    adr2 = 'a!b!x!y!z'
  736.          */
  737.         for (s = origpath; *s; s++) {
  738.             /* Does it match? */
  739.             if (strncmp(adr, s, strlen(s)) == 0) {
  740.                 char c = *s;
  741.                 *s = '\0';
  742.                 (void) strcpy(adr2, origpath);
  743.                 (void) strcat(adr2, adr);
  744.                 *s = c;
  745.                 break;
  746.             }
  747.         }
  748.  
  749.         /*
  750.          * Did I just scan the whole path without finding a match!
  751.          */
  752.         if (*s == '\0') {
  753.             /* Append the adr to the path */
  754.             (void) strcpy(adr2, origpath);
  755.             (void) strcat(adr2, adr);
  756.         }
  757.  
  758.         /* Munge the address into its shortest form and print it */
  759.         fromcvt(adr2, adr);
  760.         fprintf(rm_msgf, "%s", adr);
  761.  
  762.         if (debug)
  763.             printf("uucp munge gives %s\n", adr);
  764.     }
  765.     else {
  766.         extern    int    ap_outtype;
  767.         AP_ptr    norm;
  768.         char    *at, *brace, *fmt, *official, *p;
  769.         char    *get_official();
  770.  
  771.         /*
  772.          * Normalise the address
  773.          */
  774.         ap_outtype = AP_733;    /* Hmm. Maybe should be from channel */
  775.         norm = ap_normalize(nextdoor, (char *)NULL, the_addr, chanptr);
  776.  
  777.         /* Convert it back in to a string and output it */
  778.         (void) ap_t2s(norm, &p);
  779.         if (debug)
  780.             printf("Normalised address: %s\n", p);
  781.         /*
  782.          * Look for the last address component and re-write to the
  783.          * official form. There are probably better ways of doing this
  784.          */
  785.         at = rindex(p, '@');
  786.         fmt = "%s";
  787.         if (at) {
  788.             brace = index(at+1, '>');
  789.             if (brace) *brace = '\0';
  790.             official = get_official(at+1, (int *)NULL);
  791.             if (official) {
  792.                 *at = '\0';
  793.                 fmt = brace ? "%s@%s>" : "%s@%s";
  794.             }
  795.             else if (brace)
  796.                 *brace = '>';
  797.         }
  798.         fprintf(rm_msgf, fmt, p , official);
  799.  
  800.         if (debug) {
  801.             printf("arpa munge gives ");
  802.             printf(fmt, p, official);
  803.             printf("\n");
  804.         }
  805.  
  806.         free(p);
  807.     }
  808.     free(frees);
  809. }
  810.  
  811.  
  812.  
  813. /*
  814.  * datecvt()  --  convert UNIX ctime() style date to ARPA style
  815.  *            (change done in place)
  816.  */
  817. datecvt (date, newdate)
  818. char *date;
  819. char *newdate;
  820. {
  821.     /*
  822.      * LMCL: Changed the default timezone, when none given, to be GMT
  823.      *    012345678901234567890123456789
  824.      *    Wed Nov 18 22:23:17 1981    Unix
  825.      *    Wed, 18 Nov 81 22:23:45 GMT    ARPA
  826.      * or
  827.      *    Wed Nov 18 22:23:17 EDT 1981    Unix
  828.      *    Wed, 18 Nov 81 22:23:45 EDT    ARPA
  829.      * or even (LMCL yet again)
  830.      *    Mon Apr 23 16:50 BST 1984    Unix
  831.      */
  832.  
  833.     if (isdigit(date[0]) || date[3] == ',' || isdigit(date[5]))
  834.         (void) strcpy(newdate, date);    /* Probably already ARPA */
  835.     else if (isdigit(date[20]))
  836.         (void) sprintf(newdate, "%.3s, %.2s %.3s %.2s %.2s:%.2s:%.2s GMT",
  837.             date, date+8, date+4, date+22, date+11, date+14, date+17);
  838.     else if (isalpha(date[17]))        /* LMCL. Bum Unix format */
  839.         (void) sprintf(newdate, "%.3s, %.2s %.3s %.2s %.2s:%.2s:00 GMT",
  840.             date, date+8, date+4, date+23, date+11, date+14);
  841.     else
  842.         (void) sprintf(newdate, "%.3s, %.2s %.3s %.2s %.2s:%.2s:%.2s %.3s",
  843.             date, date+8, date+4, date+26,
  844.             date+11, date+14, date+17, date+20);
  845. }
  846.  
  847. /* */
  848. msg_stash (dest, rep)
  849. char *dest;
  850. struct rp_bufstruct *rep;
  851. {
  852.     Err_stash    *es;
  853.  
  854.     /*
  855.      * I wanted to do this using a linked list
  856.      * but there was something peculiar in the
  857.      * Orion which meant that the code failed
  858.      */
  859.     if (e_s_count >= MAXERRSTASH) {
  860.         e_s_other++;
  861.         return;
  862.     }
  863.     es = &e_stash[e_s_count++];
  864.     (void) strcpy(es->e_s_dest, dest);
  865.     es->e_s_reason = *rep;
  866. }
  867.  
  868. msg_return()
  869. {
  870.     Err_stash    *ep;
  871.     int        emax;
  872. #ifdef    CITATION
  873.     int        lines;
  874. #endif
  875.     
  876.     ml_init(YES, NO, "UUCP (rmail)", "Failed Message");
  877.     ml_adr(rm_from);
  878.     ml_aend();
  879.     ml_tinit();
  880.     ml_txt("Your message was not delivered to:\n    ");
  881.     for (ep = e_stash; ;) {
  882.         ml_txt(ep->e_s_dest);
  883.         ml_txt("\nFor the following reason:\n    ");
  884.         to80(ep->e_s_reason.rp_line);
  885.         ml_txt(ep->e_s_reason.rp_line);
  886.         if (++ep < &e_stash[e_s_count])
  887.             ml_txt("\n\nIt was also not delivered to:\n     ");
  888.         else
  889.             break;
  890.     }
  891.     if (e_s_other)
  892.         ml_txt("\nThere were other addressing errors\n\n");
  893.     e_s_other = 0;
  894.     emax = e_s_count;    /* for non delivery */
  895.     e_s_count = 0;
  896. #ifdef CITATION
  897.     ml_txt("\n\nYour message begins:\n\n");
  898.     rewind(rm_msgf);
  899.     while (fgets(linebuf, sizeof linebuf, rm_msgf) != NULL) {
  900.         ml_txt(linebuf);
  901.         if (linebuf[0] == '\n')
  902.             break;
  903.     }
  904.     if (!feof(rm_msgf)) {
  905.         lines = 0;
  906.         for (lines = CITATION; --lines > 0 &&
  907.             fgets(linebuf, sizeof linebuf, rm_msgf) != NULL;) {
  908.         
  909.             if (linebuf[0] == '\n')
  910.                 lines++;    /* truly blank lines don't count */
  911.             ml_txt(linebuf);
  912.         }
  913.         if (!feof(rm_msgf))        /* if more, give an elipses */
  914.             ml_txt("...\n");
  915.     }
  916. #else
  917.     ml_txt("\n\n--------------- Returned Mail ---------------\n\n");
  918.     rewind(rm_msgf);
  919.     ml_file(rm_msgf);
  920.     ml_txt("--------------- End of Returned Mail -------------\n");
  921. #endif
  922.     if (ml_end(OK) != OK) {
  923.     
  924.         ml_init(YES, NO, "UUCP (rmail)", "Failed Err Message");
  925.         ml_adr(supportaddr);
  926.         ml_aend();
  927.         ml_tinit();
  928.         ml_txt("Error message failed to:\n    ");
  929.         ml_txt(rm_from);
  930.         ml_txt("\n\nWhen failing to send to:\n    ");
  931.         for (ep = e_stash; ;) {
  932.             ml_txt(ep->e_s_dest);
  933.             ml_txt("\nFor the following reason:\n    ");
  934.             ml_txt(ep->e_s_reason.rp_line);
  935.             if (++ep < &e_stash[emax])
  936.                 ml_txt("\nand failing to send to:\n     ");
  937.             else
  938.                 break;
  939.         }
  940.         ml_txt("\n\n----------- Returned Mail --------------\n\n");
  941.         rewind(rm_msgf);
  942.         ml_file(rm_msgf);
  943.         ml_txt("-------- End of Returned Mail ---------------\n");
  944.         if (ml_end(OK) != OK)
  945.             bomb("Failed to returnerror message to overseer");
  946.     }
  947. }
  948.  
  949. /*
  950.  *    line formatter stolen from niftp/rtn_proc.c
  951.  */
  952. to80(from)
  953. char *from;
  954. {
  955.     register    char    *p;
  956.             char    *lastspace = NULL, *lastnl = from;
  957.  
  958.     for (p = from ; *p ; p++) {
  959.         if (*p != ' ' && *p != '\t')
  960.             continue;
  961.         if (p - lastnl > 80) {
  962.             if (lastspace != NULL) {
  963.                 *lastspace = '\n';
  964.                 lastnl = lastspace;
  965.                 if (p - lastnl <= 80) {
  966.                      lastspace = p;
  967.                     continue;
  968.                 }
  969.             }
  970.             lastspace = NULL;
  971.             *p = '\n';
  972.             lastnl = p;
  973.             continue;
  974.         }
  975.         lastspace = p;
  976.     }
  977.     if (p - lastnl > 80 && lastspace != NULL)
  978.         *lastspace = '\n';
  979. }
  980.  
  981. /*   */
  982.  
  983. xsubmit(from, argv, argc)
  984. char *from;
  985. char **argv;
  986. int argc;
  987. {
  988.     register    char            *p, *q;
  989.             struct    rp_bufstruct    thereply;
  990.             char            buf[LINESIZE];
  991.             char            subargs[LINESIZE];
  992.             char            *un_bang();
  993.             int            len;
  994.  
  995.     rewind (rm_msgf);
  996.  
  997.     if (debug) {
  998.         for (argv++; --argc > 0; argv++)
  999.             printf("arg = %s\n", un_bang(*argv));
  1000.         while (fgets(buf, LINESIZE-1, rm_msgf) != NULL)
  1001.             printf("T=%s", buf);
  1002.         if (ferror(rm_msgf))
  1003.             bomb("Error reading message file");
  1004.  
  1005.         exit(0);
  1006.     }
  1007.  
  1008.     if (rp_isbad (mm_init()) || rp_isbad (mm_sbinit ()))
  1009.         bomb("Failed to initialise submit");
  1010.  
  1011.     /* Set nameserver timeout up high -- DSH */
  1012.     (void) sprintf(subargs, "lvmti%s*hk30h", uchan);
  1013.  
  1014.     if (nextdoor[0]) {
  1015.         q = subargs + strlen(subargs);
  1016.         for (p = nextdoor; *p;) {
  1017.             if (!isprint(*p)) {
  1018.                 p++;
  1019.                 continue;
  1020.             }
  1021.             if (*p == '\'') *q++ = '\\';
  1022.             *q++ = *p++;
  1023.         }
  1024.         *q = '\0';
  1025.     }
  1026.     (void) strcat(subargs, "*");
  1027.  
  1028.     if (rp_isbad(mm_winit((char *)NULL, subargs, from)))
  1029.         bomb("mm_winit(%s, %s) failed", subargs, from);
  1030.     if (rp_isbad(mm_rrply(&thereply, &len)))
  1031.         bomb("Failed to read address reply");
  1032.     if (rp_isbad(thereply.rp_val))
  1033.         bomb("Initialization failure '%s'", thereply.rp_line);
  1034.  
  1035.     for (argv++; --argc > 0; argv++) {
  1036.         char    *name;
  1037.  
  1038.         name = un_bang(*argv);
  1039.         if (rp_isbad(mm_wadr("", name)))
  1040.             bomb("failed to write address '%s'", name);
  1041.  
  1042.         if (rp_isbad(mm_rrply(&thereply, &len)))
  1043.             bomb("Failed to read address reply");
  1044.  
  1045.         switch (rp_gval(thereply.rp_val)) {
  1046.         case RP_DOK:
  1047.         case RP_AOK:
  1048.             break;
  1049.  
  1050.         case RP_USER:
  1051.             msg_stash(name, &thereply);
  1052.             break;        /* LMCL: Was return*/
  1053.  
  1054.         default:
  1055.             bomb("Address check failure '%s' (%s)",
  1056.                 thereply.rp_line,
  1057.             rp_valstr(thereply.rp_val));
  1058.         }
  1059.     }
  1060.  
  1061.     if (rp_isbad(mm_waend()))
  1062.         bomb("Failed to write address end");
  1063.     rewind(rm_msgf);
  1064.     while (fgets(buf, LINESIZE-1, rm_msgf) != NULL)
  1065.         if (rp_isbad(mm_wtxt(buf, strlen(buf))))
  1066.             bomb("mm_wtxtfailure");
  1067.     if (ferror(rm_msgf))
  1068.         bomb("Error reading messge file");
  1069.  
  1070.     if (rp_isbad(mm_wtend()))
  1071.         bomb("mm_wtend error");
  1072.  
  1073.     if (rp_isbad(mm_rrply(&thereply, &len)))
  1074.         bomb("mm_rrply at end of message");
  1075.  
  1076.     if (rp_isbad(thereply.rp_val))
  1077.         bomb("Bad text reply val '%s' (%s)", thereply.rp_line,
  1078.                 rp_valstr(thereply.rp_val));
  1079.  
  1080.     mm_sbend();
  1081.     if (e_s_count)
  1082.         msg_return();
  1083.     return;
  1084. }
  1085.  
  1086. /*VARARGS1*/
  1087. bomb(fmt, a, b, c)
  1088. char *fmt, *a, *b, *c;
  1089. {
  1090.     if (e_s_count)
  1091.         msg_return();
  1092.     ll_log(logptr, LLOGFAT, fmt, a, b, c);
  1093.     ll_close(logptr);
  1094.     fputs("rmail: ", stderr);
  1095.     fprintf(stderr, fmt, a, b, c);
  1096.     putc('\n', stderr);
  1097.     exit(99);
  1098. }
  1099.  
  1100. /*
  1101.  * fromcvt()  --  trys to convert the UUCP style path into an ARPA style name.
  1102.  */
  1103. fromcvt(from, newfrom)
  1104. char *from, *newfrom;
  1105. {
  1106.     register    char    *cp;
  1107.     register    char    *sp;
  1108.     register    char    *off;
  1109.             char    *at, *atoff;
  1110.             char    atstore[LINESIZE], buf[LINESIZE];
  1111.             char    *get_official();
  1112.             int    saveit;
  1113.     
  1114.     if (debug)
  1115.         printf("fromcvt on %s\n", from);
  1116.  
  1117.     (void) strcpy(buf, from);
  1118.     cp = rindex(buf, '!');
  1119.     if (cp == 0) {
  1120.         (void) strcpy(newfrom, from);
  1121.         return;
  1122.     }
  1123.     /*
  1124.      *    look for @site at the end of the name
  1125.      */
  1126.     atoff = NULL;
  1127.     if ((at = index(cp, '@')) != NULL) {
  1128.         /* got one - is it followed by a ! ? */
  1129.         if (index(at+1, '!') != NULL)
  1130.              at = NULL;
  1131.         else {
  1132.             /* look up the official name of the at site */
  1133.             atoff = get_official(at+1, &saveit);
  1134.             if (atoff) {
  1135.                 if (saveit) {
  1136.                     (void) strcpy(atstore, atoff);
  1137.                     atoff = atstore;
  1138.                 }
  1139.             }
  1140.         }
  1141.     }
  1142.     *cp = 0;
  1143.     while (sp = rindex(buf, '!')) {
  1144.         /*
  1145.          * scan the path backwards looking for hosts that we
  1146.          * know about
  1147.          */
  1148.         if (off = get_official(sp+1, &saveit)) {
  1149.             if (atoff && lexequ(atoff, off))
  1150.                 (void) strcpy(newfrom, cp+1);
  1151.             else
  1152.                 (void) sprintf(newfrom, "%s@%s", cp+1, off);
  1153.             return;
  1154.         }
  1155.         else if (at && lexequ(at+1, sp+1)) {
  1156.             (void) strcpy(newfrom, cp+1);
  1157.             return;
  1158.         }
  1159.         *cp = '!';
  1160.         cp = sp;
  1161.         *cp = 0;
  1162.     }
  1163.     if (off = get_official(buf, (int *)NULL))
  1164.         (void) sprintf(newfrom, "%s@%s", cp+1, off);
  1165.     else
  1166.         (void) sprintf(newfrom, "%s@%s", cp+1, buf);
  1167. }
  1168.  
  1169. /*
  1170.  *    This piece added by Peter Collinson (UKC)
  1171.  *    rather than just making rmail submit into the uucp channel which
  1172.  *    is hard coded. First look up in a table 'rmail.chans' for entries
  1173.  *    of the form
  1174.  *    nextdoor:channel
  1175.  *    If the name exists then use that channel otherwise just default to
  1176.  *    the uucp channel. This is for authorisation purposes really
  1177.  */
  1178. set_channel(nxt)
  1179. char *nxt;
  1180. {
  1181.     Table    *tb_rmchans;
  1182.     char    tuchan[LINESIZE];
  1183.  
  1184. #ifdef DEBUG
  1185.     ll_log(logptr, LLOGBTR, "set_channel(%s)", nxt);
  1186. #endif
  1187.     /*
  1188.      * set default
  1189.      */
  1190.     (void) strcpy(uchan, Uchan);
  1191.     
  1192.     if ((tb_rmchans = tb_nm2struct("rmail.chans")) != (Table *)NOTOK) {
  1193.         /*
  1194.          * Well, we have the table. Extract name of current channel
  1195.          * from list of channels
  1196.          */
  1197.         if (tb_k2val(tb_rmchans, TRUE, nxt, tuchan) == OK)
  1198.             (void) strcpy(uchan, tuchan);
  1199.     }
  1200.  
  1201.     /*
  1202.      * Get the channel attributes
  1203.      */
  1204.     if ((chanptr = ch_nm2struct(uchan)) == (Chan *)NOTOK) {
  1205.         /* fail safe - not sure that we should do this */
  1206.         /* find the default channel */
  1207.         if ((chanptr = ch_nm2struct(Uchan)) == (Chan *)NOTOK)
  1208.             bomb("Cannot look up channel '%s'\n", uchan);
  1209.     }
  1210. }
  1211.  
  1212. /*
  1213.  *    Check if the from machine which we have constructed actually
  1214.  *    contains two references to the same machine of the form
  1215.  *    site.uucp!site.??.uk!something
  1216.  *    if so replace by the single official name
  1217.  *
  1218.  * This can fail can't it? -- DSH
  1219.  */
  1220. domain_cross(route)
  1221. char *route;
  1222. {
  1223.     char    *enda, *endsecond, *official, *second;
  1224.     char    *host_equal();
  1225.  
  1226.     second = index(route, '!');
  1227.     if (second == (char *)NULL)
  1228.         return;
  1229.     second++;
  1230.     endsecond = index(second, '!');
  1231.     if (endsecond == (char *)NULL)
  1232.         return;
  1233.     second[-1] = '\0';
  1234.     *endsecond = '\0';
  1235.     if (official = host_equal(route, second)) {
  1236.         enda = strdup(endsecond+1);
  1237.         (void) strcpy(route, official);
  1238.         (void) strcat(route, "!");
  1239.         (void) strcat(route, enda);
  1240.         free(enda);
  1241.     }
  1242.     else {
  1243.         second[-1] = '!';
  1244.         *endsecond = '!';
  1245.     }
  1246. }
  1247.  
  1248. /*
  1249.  *    host_equal
  1250.  *    a routine to see whether the strings which are input
  1251.  *    are in fact the same host
  1252.  *    Returns the official name if they are and a null pointer if not
  1253.  */
  1254. char *
  1255. host_equal(n1, n2)
  1256. char *n1, *n2;
  1257. {
  1258.     static    char    res1[LINESIZE];
  1259.         char    *o1, *o2;
  1260.         char    *get_official();
  1261.         int    saveit;
  1262.     
  1263.     o1 = get_official(n1, &saveit);
  1264.     if (o1 == NULL)
  1265.         return(NULL);
  1266.     if (saveit) {
  1267.         (void) strcpy(res1, o1);
  1268.         o1 = res1;
  1269.     }
  1270.     o2 = get_official(n2, &saveit);
  1271.     if (o2 == NULL)
  1272.         return(NULL);
  1273.     if (lexequ(o1, o2) == TRUE)
  1274.         return(o1);
  1275.     return(NULL);
  1276. }
  1277.  
  1278. /*   */
  1279.  
  1280. /*
  1281.  *    These routines perform some caching on the host name/official
  1282.  *    name pair in and attempt to reduce overheads
  1283.  */
  1284. Official *off_cache;            /* pointer to the base of the cache */
  1285. #define OFFICIALSIZE    50        /* Chunk size for the cache */
  1286. int    off_size;            /* current size of the cache */
  1287. int    off_used;            /* current set which are used */
  1288. /*
  1289.  *    get a host from the domain tables
  1290.  *    if the cache has fallen over due to lack of memory
  1291.  *    then the saveit switch will be set showing that the
  1292.  *    address is in static memory
  1293.  */
  1294. char *
  1295. get_official(host, saveit)
  1296. char *host;
  1297. int *saveit;                /* set if the cache is not working */
  1298. {
  1299.     register    Official    *off;
  1300.     static        char        res[LINESIZE];
  1301.             Domain        *dmn;
  1302.             Domain        *dm_v2route();
  1303.             Dmn_route    rbuf, *route = &rbuf;
  1304.             int        sadummy;
  1305.  
  1306.     if (debug)
  1307.         printf("get_official called for %s\n", host);
  1308.  
  1309.     if (saveit == (int *)NULL)
  1310.         saveit = &sadummy;
  1311.     *saveit = 0;            /* assume cache is working */
  1312.     
  1313.     if (off_size == 0) {
  1314.         off_cache = (Official *)malloc(OFFICIALSIZE*sizeof(Official));
  1315.         off_size = OFFICIALSIZE;
  1316.         if (debug)
  1317.             printf("Cache%sset - %d items\n",
  1318.                 off_cache ? " " : " not " ,
  1319.                 off_cache ? off_size : 0);
  1320.     }
  1321.  
  1322.     /*
  1323.      * If we have managed to get space for the cache 
  1324.      * search it for the name
  1325.      */
  1326.     if (off_cache == (Official *)NULL)
  1327.         *saveit = 1;
  1328.     else {
  1329.         for (off = off_cache; off < &off_cache[off_used]; off++)
  1330.             if (*host == *off->o_host && lexequ(host, off->o_host)) {
  1331.                 if (debug)
  1332.                     printf("Cache hit - %s\n", off->o_official);
  1333.                 return(off->o_official);
  1334.             }
  1335.     }
  1336.  
  1337.     /*
  1338.      * Bad news - we must do something to get the information
  1339.      */
  1340.     dmn = dm_v2route(host, res, route);
  1341.     if (dmn == (Domain *)NOTOK) {
  1342.         if (debug)
  1343.             printf("%s not in tables\n", host);
  1344.         return(NULL);
  1345.     }
  1346.  
  1347.     /*
  1348.      * we have a hit in the tables
  1349.      * save it in the cache
  1350.      */
  1351.     if (off_used >= off_size) {
  1352.         off_size += OFFICIALSIZE;
  1353.         /*NOSTRICT*/
  1354.         off_cache = (Official *)realloc(off_cache,off_size * sizeof(Official));
  1355.         if (debug) {
  1356.             if (off_cache)
  1357.                 printf("Cache grown to %d items\n", off_size);
  1358.             else
  1359.                 printf("Cache grow failed\n");
  1360.         }
  1361.     }
  1362.  
  1363.     if (off_cache) {
  1364.         off = &off_cache[off_used];
  1365.         if ((off->o_host = strdup(host)) &&
  1366.             (off->o_official = strdup(res))) {
  1367.             off_used++;
  1368.             if (debug) {
  1369.                 printf("%s - %s stored in cache - cachesize %d\n",
  1370.                     off->o_host,
  1371.                     off->o_official,
  1372.                     off_used);
  1373.             }
  1374.             return(off->o_official);
  1375.         }    
  1376.     }
  1377.  
  1378.     *saveit = 1;
  1379.     if (debug)
  1380.         printf("%s - %s not cached\n", host, res);
  1381.     return(res);
  1382. }
  1383.  
  1384. /*
  1385.  *    Added Peter Collinson June 1987
  1386.  *    take a possibly pure bang address and convert it into
  1387.  *    733 % @ format
  1388.  *
  1389.  *
  1390.  *    The above is unnecessary and triggers problems, such as
  1391.  *    propagation of
  1392.  *        rmail oddjob!ncar!mailrus!utai!watmath!looking!brad
  1393.  *    as
  1394.  *        rmail ncar!brad%looking%watmath%utai%mailrus
  1395.  *
  1396.  *    Instead of converting    x!y!z!user to user%z%y@x, convert
  1397.  *    it into y!z!user@x.  This is perfectly fine for systems
  1398.  *    that obey RFC-976 (which MMDF is one of).
  1399.  *        -- JG
  1400.  */
  1401. char *
  1402. un_bang(adr)
  1403. register char *adr;
  1404. {
  1405.     register    char    *bangptr, *dest;
  1406.     
  1407.     /*
  1408.      * get out of anything other than bangs
  1409.      */
  1410.     if (index(adr, '@') || index(adr, '%') || index(adr, ':'))
  1411.         return(adr);
  1412.     /*
  1413.      * get out if no '!'
  1414.      */
  1415.     if ((bangptr = index(adr, '!')) == (char *)NULL)
  1416.         return(adr);
  1417.  
  1418.     /*
  1419.      * convert "a!b!c!user" into "b!c!user@a"
  1420.      */
  1421.     dest = malloc(strlen(adr+16));
  1422.     (void) strcpy(dest, bangptr+1);
  1423.     (void) strcat(dest, "@");
  1424.     *bangptr = '\0';
  1425.     (void) strcat(dest, adr);
  1426.     (void) strcpy(adr, dest);
  1427.     free(dest);
  1428.     return(adr);
  1429. }
  1430.